#include "lineDart.h"
#include "spokeDart.h"
#include "gpuLineDart.h"
#include "gpuSpokeDart.h"
#include "gpuPlaneSpokeDart.h"
#include "gpuPlaneSpokeDartNoLoop.h"
#include "gpuDegenSpokeDart.h"
#include "vectorD.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vector>
#include <list>
#include <time.h>
#include "randUtil.h"
#include "cuda_defs.h"
#include "dataType.h"

using namespace std;

void cpuLineDarts(unsigned int d, unsigned int m, DATA_TYPE radius);
void cpuSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius);
void gpuLineDarts(unsigned int d, unsigned int m, DATA_TYPE radius);
void gpuSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius);
void gpuPlaneSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius);
void gpuDegenSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius);
void gpuPlaneSpokeDartsNoLoop(unsigned int d, unsigned int m, DATA_TYPE radius);

enum DART_TYPE
{
	LINE_DART = 0,
	SPOKE_DART = 1,
	LINE_DART_GPU = 2,
	SPOKE_DART_GPU = 3,
	PLANE_SPOKE_DART_GPU = 4,
	DEGEN_SPOKE_DART_GPU = 5,
	DEGEN_PLANE_SPOKE_DART_GPU = 6,
	PLANE_SPOKE_DART_NO_LOOP_GPU = 7,
	NUM_DART_TYPES,
};

double timeOut = 1000.0;

//#define USE_NSIGHT_DEBUG
int main(int argc, char* argv[])
{

#ifndef USE_NSIGHT_DEBUG
	if (argc < 5)
	{
		printf("Usage : line_darts <dart_type> <dimensions> <radius> <m> <timeOut>\n");
		printf("dart_type : 0 - line dart\n");
		printf("            1 - spoke dart\n");
		printf("            2 - GPU line dart\n");
		printf("            3 - GPU spoke dart\n");
		printf("            4 - GPU plane spoke dart\n");
		printf("            5 - GPU degen spoke dart\n");
		printf("            6 - GPU degen plane spoke dart\n");
		printf("            7 - GPU plane spoke dart no loop\n");
		return 1;
	}
	unsigned int dartType = atoi(argv[1]);
	unsigned int d = atoi(argv[2]);
	DATA_TYPE r = (DATA_TYPE)atof(argv[3]);
	unsigned int m = atoi(argv[4]);

	if (argc >= 6)
		timeOut = atof(argv[5]);
#else
	// for cuda debug
	unsigned int dartType = PLANE_SPOKE_DART_GPU;
	unsigned int d = 4;
	DATA_TYPE r = 0.05f;
	unsigned int m = 200;
	timeOut = 720000.0;
#endif

	VectorD::setDimensions(d);

	const char* dartName[] = {
		"CPU Line Darts",
		"CPU Spoke Darts",
		"GPU Line Darts",
		"GPU Spoke Darts",
		"GPU Plane Spoke Darts",
		"GPU Degen Spoke Darts",
		"GPU Degen Plane Spoke Darts",
		"GPU Plane Spoke Darts No Loop",};
	if (dartType >= NUM_DART_TYPES)
		dartType = 0;
	printf("Use %s\n", dartName[dartType]);
	printf("Dimensions : %d\n", d);
	printf("r_f : %f\n", r);
	printf("m : %d\n", m);
	printf("timeout : after %f seconds\n\n", timeOut);

	switch (dartType)
	{
	case LINE_DART:
		cpuLineDarts(d, m, r);
		break;

	case SPOKE_DART:
		cpuSpokeDarts(d, m, r);
		break;

	case LINE_DART_GPU:
		gpuLineDarts(d, m, r);
		break;

	case SPOKE_DART_GPU:
		gpuSpokeDarts(d, m, r);
		break;

	case PLANE_SPOKE_DART_GPU:
		gpuPlaneSpokeDarts(d, m, r);
		break;

	case DEGEN_SPOKE_DART_GPU:
		gpuDegenSpokeDarts(d, m, r);
		break;

	case PLANE_SPOKE_DART_NO_LOOP_GPU:
		gpuPlaneSpokeDartsNoLoop(d, m, r);
		break;
	}

	return 0;
}

void cpuLineDarts(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	vector<VectorD> samples;
	samples.reserve(200000);

	clock_t totalTime, begin, updateIntervalTime = 0, pickPointTime = 0, printTime = 0;

	int failCount = 0;
	int i = 0;
	Dart* dart = new LineDart();

	int printSize = 10000;
	double printSizeAdjustTime = 10.0;

	clock_t startTime = clock();
	while (true)
	{
		dart->reset(d);

		begin = clock();
		dart->updateIntervals(samples, radius);
		updateIntervalTime += clock() - begin;

		VectorD newPoint;
		begin = clock();
		bool success = dart->pickPoint(newPoint);
		pickPointTime += clock() - begin;
		
		if (success)
		{
			samples.push_back(newPoint);
			failCount = 0;
		}
		else
		{
			++failCount;
		}
		++i;

		if (i % 10000 == 0)
		{
			printf("[%d] %d samples %f\n", i, samples.size(), (clock() - startTime)/(double)CLOCKS_PER_SEC);
		}

		if (i >= 4000000)
		{
			printf("[%d] %d samples %f\n", i, samples.size(), (clock() - startTime)/(double)CLOCKS_PER_SEC);
			break;
		}
	}
	totalTime = clock() - startTime;

	printf("validation\n");
	for (unsigned int i = 0; i < samples.size(); ++i)
	{
		for (unsigned int j = i + 1; j < samples.size(); ++j)
		{
			DATA_TYPE sq = (samples[i] - samples[j]).getSquaredNorm();
			if (sq < radius * radius - DATA_TYPE_EPS)
			{
				printf("!!!invalid %d-%d dist : %f\n", i, j, sqrt(sq));
			}
		}
	}

	printf("m : %d\n", m);
	printf("Total Iterations : %d\n", i);
	printf("# of Points : %d\n", samples.size());
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);

	delete dart;
}

void cpuSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	vector<VectorD> samples;
	samples.reserve(200000);
	vector<VectorD> neighbors;
	neighbors.reserve(10000);

	clock_t totalTime, begin, updateIntervalTime = 0, pickPointTime = 0, neighborTime = 0, printTime = 0;

	Dart* dart = new SpokeDart();
	
	// set first point
	VectorD firstSample; 
	for (unsigned int i = 0; i < d; ++i)
		firstSample[i] = RandUtil::rand0To1();
	samples.push_back(firstSample);

	printf("[%d] [", 0);
	for (unsigned int i = 0; i < VectorD::getDimensions(); ++i)
		printf("%f ", firstSample[i]);
	printf("] added\n");

	int printSize = 10000;
	double printSizeAdjustTime = 10.0;
	unsigned int nextShow = 10000;

	DATA_TYPE sqRadius = radius * radius;
	unsigned int iterations = 0;
	clock_t startTime = clock();
	for (unsigned int i = 0; i < samples.size(); ++i)
	{
		VectorD& frontSample = samples[i];
		unsigned int failCount = 0;

		// compute short spoke neighbors of frontSample
		begin = clock();
		neighbors.clear();
		for (unsigned int j = 0; j < samples.size(); ++j)
		{
			const VectorD& otherSample = samples[j];

			if (i == j)
				continue;

			if ((frontSample - otherSample).getSquaredNorm() < 9 * sqRadius)
				neighbors.push_back(otherSample);
		}
		neighborTime += clock() - begin;
		
		unsigned int j = 0;
		while (failCount < m)
		{
			dart->reset(d, frontSample, radius);

			begin = clock();
			dart->updateIntervals(neighbors, radius);
			updateIntervalTime += clock() - begin;

			VectorD newPoint;
			begin = clock();
			bool success = dart->pickPoint(newPoint);
			pickPointTime += clock() - begin;

			if (success)
			{
				failCount = 0;
				samples.push_back(newPoint);
				neighbors.push_back(newPoint);
			}
			else
			{
				++failCount; 
			}
			++j;

			if (samples.size() >= nextShow)
			{
				printf("[%d] %d samples %f\n", iterations, samples.size(), (clock() - startTime)/(double)CLOCKS_PER_SEC);
				nextShow += 10000;
			}
			if (samples.size() >= 193000)
			{
				printf("[%d] %d samples %f\n", iterations, samples.size(), (clock() - startTime)/(double)CLOCKS_PER_SEC);
				break;
			}
			++iterations;
		}

		if (samples.size() >= 193000)
			break;
	}
	totalTime = clock() - startTime;

	printf("validation\n");
	for (unsigned int i = 0; i < samples.size(); ++i)
	{
		for (unsigned int j = i + 1; j < samples.size(); ++j)
		{
			DATA_TYPE sq = (samples[i] - samples[j]).getSquaredNorm();
			if (sq < radius * radius - DATA_TYPE_EPS)
			{
				printf("!!!invalid %d(%f, %f)-%d(%f,%f) dist : %f\n", i, samples[i][0], samples[i][1], 
					j, samples[j][0], samples[j][1], sqrt(sq));
			}
		}
	}

	printf("# of Points : %d\n", samples.size());
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Neighbor Computation Time : %f\n", neighborTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);

	delete dart;
}

void gpuLineDarts(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	int counter = 0;
	int printSize = 10;
	double printSizeAdjustTime = 10.0;
	int oldSampleSize = 0;

	cudaEvent_t startEvent, stopEvent;
	cudaEventCreate(&startEvent);
	cudaEventCreate(&stopEvent);
	float msUpdateInterval, msUpdateInterval2, msSetup, msPickPoint, msAdd;

	clock_t totalTime, iterationBegin, begin, setupTime = 0, updateIntervalTime = 0, updateInterval2Time = 0, pickPointTime = 0, addSampleTime = 0, printTime = 0, elapsed;
	clock_t setupTimeDelta, updateIntervalTimeDelta, updateInterval2TimeDelta, pickPointTimeDelta, addSampleTimeDelta;
	GPULineDart dart;
	if (!dart.init(m, radius, d))
		return;
	clock_t startTime = elapsed = clock();

	dart.setValidateResults(false);

	int iterations = 0;
	int numBlocks = dart.getUseMultipleBlocks() ? NUM_BLOCKS : 1;
	while (true)
	{
		iterationBegin = begin = clock();
		__cudaMeasureTimeStart(startEvent);
		dart.setupDarts();
		__cudaMeasureTimeEnd(&msSetup, startEvent, stopEvent);
		setupTime += clock() - begin;

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals();
		updateIntervalTimeDelta = clock() - begin;
		updateIntervalTime += updateIntervalTimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval, startEvent, stopEvent);
		
		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals2();
		updateInterval2TimeDelta = clock() - begin;
		updateInterval2Time += updateInterval2TimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval2, startEvent, stopEvent);
		
		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.pickPoint();
		pickPointTime += clock() - begin;
		__cudaMeasureTimeEnd(&msPickPoint, startEvent, stopEvent);
		
		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.addSamples();
		addSampleTimeDelta = clock() - begin;
		addSampleTime += addSampleTimeDelta;
		__cudaMeasureTimeEnd(&msAdd, startEvent, stopEvent);

		iterations += NUM_THREADS * numBlocks;

		++counter;

		const int CHECK_POINT = 100;
		if (counter % CHECK_POINT == 0)
		{
			clock_t time = clock();
			
			int numSamples = dart.getNumGeneratedSamples();
			int increased = numSamples - oldSampleSize;
			printf("[%d] %d samples %f sec hit: %d / %d\n", iterations, numSamples, (time - startTime)/(double)CLOCKS_PER_SEC, 
				increased, NUM_THREADS * numBlocks * CHECK_POINT);

#ifdef CUDA_MEASURE_TIME
			double trialsPerSec = (NUM_THREADS * numBlocks * CHECK_POINT) / ((time - elapsed)/(double)CLOCKS_PER_SEC);
			printf("%f trials / sec\n", trialsPerSec);
			printf("Total iteration time : %f ms\n", (time - elapsed)/(double)CLOCKS_PER_SEC / CHECK_POINT * 1000);
			printf("Setup Time : %f ms\n", msSetup);
			printf("Update Interval Time : %f ms\n", msUpdateInterval);
			printf("Update Interval 2 Time : %f ms\n", msUpdateInterval2);
			printf("Pick Time : %f ms\n", msPickPoint);
			printf("Add Sample Time : %f ms\n", msAdd);
#endif

			if ((numSamples - oldSampleSize) < CHECK_POINT * 100 && !dart.getUseMultipleBlocks())
			{
				printf("start to use multiple blocks\n");
				dart.setUseMultipleBlocks(true);
				numBlocks = NUM_BLOCKS;
			}
			oldSampleSize = numSamples;

			elapsed = time;

			if((time - printTime)/(double)CLOCKS_PER_SEC > timeOut)
				break;
		}
	}
	totalTime = clock() - startTime;

	cudaEventDestroy(startEvent);
	cudaEventDestroy(stopEvent);

	dart.downloadSamples();
	printf("%d Iterations\n", iterations);
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval2 Time : %f\n", updateInterval2Time / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);
	printf("Add Sample Time : %f\n", addSampleTime / (double)CLOCKS_PER_SEC);
}

void gpuSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	int counter = 0;
	int printSize = 10;
	double printSizeAdjustTime = 10.0;
	int oldSampleSize = 0;

	cudaEvent_t startEvent, stopEvent;
	cudaEventCreate(&startEvent);
	cudaEventCreate(&stopEvent);
	float msUpdateInterval, msUpdateInterval2, msSetup, msPickPoint, msAdd;

	clock_t totalTime, iterationBegin, begin, setupTime = 0, updateIntervalTime = 0, updateInterval2Time = 0, pickPointTime = 0, addSampleTime = 0, printTime = 0, elapsed;
	clock_t setupTimeDelta, updateIntervalTimeDelta, updateInterval2TimeDelta, pickPointTimeDelta, addSampleTimeDelta;
	GPUSpokeDart dart;
	if (!dart.init(m, radius, d))
		return;
	clock_t startTime = elapsed = clock();

	int iterations = 0;
	//dart.setUseMultipleBlocks(true);
	int numBlocks = dart.getUseMultipleBlocks() ? NUM_BLOCKS : 1;
	while (true)
	{
		iterationBegin = begin = clock();
		__cudaMeasureTimeStart(startEvent);
		dart.setupDarts();
		__cudaMeasureTimeEnd(&msSetup, startEvent, stopEvent);
		setupTime += clock() - begin;

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals();
		updateIntervalTimeDelta = clock() - begin;
		updateIntervalTime += updateIntervalTimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval, startEvent, stopEvent);
		
		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals2();
		updateInterval2TimeDelta = clock() - begin;
		updateInterval2Time += updateInterval2TimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval2, startEvent, stopEvent);
		
		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.pickPoint();
		pickPointTime += clock() - begin;
		__cudaMeasureTimeEnd(&msPickPoint, startEvent, stopEvent);
		
		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.addSamples();
		addSampleTimeDelta = clock() - begin;
		addSampleTime += addSampleTimeDelta;
		__cudaMeasureTimeEnd(&msAdd, startEvent, stopEvent);

		iterations += NUM_THREADS * numBlocks;

		++counter;

		const int CHECK_POINT = 100;
		if (counter % CHECK_POINT == 0)
		{
			clock_t time = clock();
			
			int numSamples = dart.getNumGeneratedSamples();
			int increased = numSamples - oldSampleSize;
			printf("[%d] %d samples %f sec hit: %d / %d nextFront: %d / %d front : %d\n", iterations, numSamples, (time - startTime)/(double)CLOCKS_PER_SEC, 
				increased, NUM_THREADS * numBlocks * CHECK_POINT,
				dart.getNextFront(), numSamples, dart.getNumFront());
#ifdef CUDA_MEASURE_TIME
			double trialsPerSec = (NUM_THREADS * numBlocks * CHECK_POINT) / ((time - elapsed)/(double)CLOCKS_PER_SEC);
			printf("%f trials / sec\n", trialsPerSec);
			printf("Total iteration time : %f ms\n", (time - elapsed)/(double)CLOCKS_PER_SEC / CHECK_POINT * 1000);
			printf("Setup Time : %f ms\n", msSetup);
			printf("Update Interval Time : %f ms\n", msUpdateInterval);
			printf("Update Interval 2 Time : %f ms\n", msUpdateInterval2);
			printf("Pick Time : %f ms\n", msPickPoint);
			printf("Add Sample Time : %f ms\n", msAdd);
#endif

			if ((numSamples - oldSampleSize) < CHECK_POINT * 10 && !dart.getUseMultipleBlocks())
			{
				printf("start to use multiple blocks\n");
				dart.setUseMultipleBlocks(true);
				numBlocks = NUM_BLOCKS;
			}
			oldSampleSize = numSamples;
			elapsed = time;

			if((time - printTime)/(double)CLOCKS_PER_SEC > timeOut)
				break;
		}
		if (dart.terminationCheck())
			break;
	}
	totalTime = clock() - startTime;

	cudaEventDestroy(startEvent);
	cudaEventDestroy(stopEvent);

	dart.downloadSamples();
	printf("%d Iterations\n", iterations);
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval2 Time : %f\n", updateInterval2Time / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);
	printf("Add Sample Time : %f\n", addSampleTime / (double)CLOCKS_PER_SEC);
}

void gpuPlaneSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	int counter = 0;
	int printSize = 10;
	double printSizeAdjustTime = 10.0;
	int oldSampleSize = 0;

	cudaEvent_t startEvent, stopEvent;
	cudaEventCreate(&startEvent);
	cudaEventCreate(&stopEvent);
	float msUpdateInterval, msUpdateInterval2, msSetup, msPickPoint, msAdd;

	clock_t totalTime, iterationBegin, begin, setupTime = 0, updateIntervalTime = 0, updateInterval2Time = 0, pickPointTime = 0, addSampleTime = 0, printTime = 0, elapsed;
	clock_t setupTimeDelta, updateIntervalTimeDelta, updateInterval2TimeDelta, pickPointTimeDelta, addSampleTimeDelta;
	GPUPlaneSpokeDart dart;
	if (!dart.init(m, radius, d))
		return;
	clock_t startTime = elapsed = clock();

	int iterations = 0;
	//dart.setUseMultipleBlocks(true);
	int numBlocks = dart.getUseMultipleBlocks() ? NUM_BLOCKS : 1;
	while (true)
	{
		iterationBegin = begin = clock();
		__cudaMeasureTimeStart(startEvent);
		dart.setupDarts();
		__cudaMeasureTimeEnd(&msSetup, startEvent, stopEvent);
		setupTime += clock() - begin;

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals();
		updateIntervalTimeDelta = clock() - begin;
		updateIntervalTime += updateIntervalTimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals2();
		updateInterval2TimeDelta = clock() - begin;
		updateInterval2Time += updateInterval2TimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval2, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.pickPoint();
		pickPointTime += clock() - begin;
		__cudaMeasureTimeEnd(&msPickPoint, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.addSamples();
		addSampleTimeDelta = clock() - begin;
		addSampleTime += addSampleTimeDelta;
		__cudaMeasureTimeEnd(&msAdd, startEvent, stopEvent);

		iterations += NUM_THREADS * NUM_BLOCKS;

		++counter;

		const int CHECK_POINT = 100;
		if (counter % CHECK_POINT == 0)
		{
			clock_t time = clock();

			int numSamples = dart.getNumGeneratedSamples();
			int increased = numSamples - oldSampleSize;
			printf("[%d] %d samples %f sec hit: %d / %d nextFront: %d / %d front : %d\n", iterations, numSamples, (time - startTime)/(double)CLOCKS_PER_SEC, 
				increased, NUM_THREADS * NUM_BLOCKS * CHECK_POINT,
				dart.getNextFront(), numSamples, dart.getNumFront());
#ifdef CUDA_MEASURE_TIME
			double trialsPerSec = (NUM_THREADS * NUM_BLOCKS * CHECK_POINT) / ((time - elapsed)/(double)CLOCKS_PER_SEC);
			printf("%f trials / sec\n", trialsPerSec);
			printf("Total iteration time : %f ms\n", (time - elapsed)/(double)CLOCKS_PER_SEC / CHECK_POINT * 1000);
			printf("Setup Time : %f ms\n", msSetup);
			printf("Update Interval Time : %f ms\n", msUpdateInterval);
			printf("Update Interval 2 Time : %f ms\n", msUpdateInterval2);
			printf("Pick Time : %f ms\n", msPickPoint);
			printf("Add Sample Time : %f ms\n", msAdd);
#endif
			
			if ((numSamples - oldSampleSize) < CHECK_POINT * 100 && !dart.getUseMultipleBlocks())
			{
				printf("start to use multiple blocks\n");
				dart.setUseMultipleBlocks(true);
				numBlocks = NUM_BLOCKS;
			}
			
			oldSampleSize = numSamples;
			elapsed = time;

			if((time - printTime)/(double)CLOCKS_PER_SEC > timeOut)
				break;
		}
		if (dart.terminationCheck())
			break;
	}
	totalTime = clock() - startTime;

	cudaEventDestroy(startEvent);
	cudaEventDestroy(stopEvent);

	dart.downloadSamples();
	printf("%d Iterations\n", iterations);
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Setup Time : %f\n", setupTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval2 Time : %f\n", updateInterval2Time / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);
	printf("Add Sample Time : %f\n", addSampleTime / (double)CLOCKS_PER_SEC);
}

void gpuDegenSpokeDarts(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	int counter = 0;
	int printSize = 10;
	double printSizeAdjustTime = 10.0;
	int oldSampleSize = 0;

	cudaEvent_t startEvent, stopEvent;
	cudaEventCreate(&startEvent);
	cudaEventCreate(&stopEvent);
	float msUpdateInterval, msUpdateInterval2, msSetup, msPickPoint, msAdd;

	clock_t totalTime, iterationBegin, begin, setupTime = 0, updateIntervalTime = 0, updateInterval2Time = 0, pickPointTime = 0, addSampleTime = 0, printTime = 0, elapsed;
	clock_t setupTimeDelta, updateIntervalTimeDelta, updateInterval2TimeDelta, pickPointTimeDelta, addSampleTimeDelta;
	GPUDegenSpokeDart dart;
	if (!dart.init(m, radius, d))
		return;
	clock_t startTime = elapsed = clock();

	int iterations = 0;
	//dart.setUseMultipleBlocks(true);
	int numBlocks = dart.getUseMultipleBlocks() ? NUM_BLOCKS : 1;
	while (true)
	{
		iterationBegin = begin = clock();
		__cudaMeasureTimeStart(startEvent);
		dart.setupDarts();
		__cudaMeasureTimeEnd(&msSetup, startEvent, stopEvent);
		setupTime += clock() - begin;

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals();
		updateIntervalTimeDelta = clock() - begin;
		updateIntervalTime += updateIntervalTimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals2();
		updateInterval2TimeDelta = clock() - begin;
		updateInterval2Time += updateInterval2TimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval2, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.pickPoint();
		pickPointTime += clock() - begin;
		__cudaMeasureTimeEnd(&msPickPoint, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.addSamples();
		addSampleTimeDelta = clock() - begin;
		addSampleTime += addSampleTimeDelta;
		__cudaMeasureTimeEnd(&msAdd, startEvent, stopEvent);

		iterations += NUM_THREADS * numBlocks;

		++counter;
		
		const int CHECK_POINT = 100;
		if (counter % CHECK_POINT == 0)
		{
			clock_t time = clock();

			int numSamples = dart.getNumGeneratedSamples();
			int increased = numSamples - oldSampleSize;
			printf("[%d] %d samples %f sec hit: %d / %d nextFront: %d / %d front : %d\n", iterations, numSamples, (time - startTime)/(double)CLOCKS_PER_SEC, 
				increased, NUM_THREADS * numBlocks * CHECK_POINT,
				dart.getNextFront(), numSamples, dart.getNumFront());
#ifdef CUDA_MEASURE_TIME
			double trialsPerSec = (NUM_THREADS * numBlocks * CHECK_POINT) / ((time - elapsed)/(double)CLOCKS_PER_SEC);
			printf("%f trials / sec\n", trialsPerSec);
			printf("Total iteration time : %f ms\n", (time - elapsed)/(double)CLOCKS_PER_SEC / CHECK_POINT * 1000);
			printf("Setup Time : %f ms\n", msSetup);
			printf("Update Interval Time : %f ms\n", msUpdateInterval);
			printf("Update Interval 2 Time : %f ms\n", msUpdateInterval2);
			printf("Pick Time : %f ms\n", msPickPoint);
			printf("Add Sample Time : %f ms\n", msAdd);
#endif

			if ((numSamples - oldSampleSize) < CHECK_POINT * 100 && !dart.getUseMultipleBlocks())
			{
				printf("start to use multiple blocks\n");
				dart.setUseMultipleBlocks(true);
				numBlocks = NUM_BLOCKS;
			}
			oldSampleSize = numSamples;
			elapsed = time;

			if((time - printTime)/(double)CLOCKS_PER_SEC > timeOut)
				break;
		}
		
		if (dart.terminationCheck())
			break;
	}
	totalTime = clock() - startTime;

	cudaEventDestroy(startEvent);
	cudaEventDestroy(stopEvent);

	dart.downloadSamples();
	printf("%d Iterations\n", iterations);
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval2 Time : %f\n", updateInterval2Time / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);
	printf("Add Sample Time : %f\n", addSampleTime / (double)CLOCKS_PER_SEC);
}

void gpuPlaneSpokeDartsNoLoop(unsigned int d, unsigned int m, DATA_TYPE radius)
{
	int counter = 0;
	int printSize = 10;
	double printSizeAdjustTime = 10.0;
	int oldSampleSize = 0;

	cudaEvent_t startEvent, stopEvent;
	cudaEventCreate(&startEvent);
	cudaEventCreate(&stopEvent);
	float msUpdateInterval, msUpdateInterval2, msSetup, msPickPoint, msAdd;

	clock_t totalTime, iterationBegin, begin, setupTime = 0, updateIntervalTime = 0, updateInterval2Time = 0, pickPointTime = 0, addSampleTime = 0, printTime = 0, elapsed;
	clock_t setupTimeDelta, updateIntervalTimeDelta, updateInterval2TimeDelta, pickPointTimeDelta, addSampleTimeDelta;
	GPUPlaneSpokeDartNoLoop dart;
	if (!dart.init(m, radius, d))
		return;
	clock_t startTime = elapsed = clock();

	int iterations = 0;
	//dart.setUseMultipleBlocks(true);
	int numBlocks = dart.getUseMultipleBlocks() ? NUM_BLOCKS : 1;
	while (true)
	{
		iterationBegin = begin = clock();
		__cudaMeasureTimeStart(startEvent);
		dart.setupDarts();
		__cudaMeasureTimeEnd(&msSetup, startEvent, stopEvent);
		setupTime += clock() - begin;

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals();
		updateIntervalTimeDelta = clock() - begin;
		updateIntervalTime += updateIntervalTimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.updateIntervals2();
		updateInterval2TimeDelta = clock() - begin;
		updateInterval2Time += updateInterval2TimeDelta;
		__cudaMeasureTimeEnd(&msUpdateInterval2, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.pickPoint();
		pickPointTime += clock() - begin;
		__cudaMeasureTimeEnd(&msPickPoint, startEvent, stopEvent);

		__cudaMeasureTimeStart(startEvent);
		begin = clock();
		dart.addSamples();
		addSampleTimeDelta = clock() - begin;
		addSampleTime += addSampleTimeDelta;
		__cudaMeasureTimeEnd(&msAdd, startEvent, stopEvent);

		iterations += NUM_THREADS * NUM_BLOCKS;

		++counter;

		const int CHECK_POINT = 100;
		if (counter % CHECK_POINT == 0)
		{
			clock_t time = clock();

			int numSamples = dart.getNumGeneratedSamples();
			int increased = numSamples - oldSampleSize;
			printf("[%d] %d samples %f sec hit: %d / %d nextFront: %d / %d front : %d\n", iterations, numSamples, (time - startTime)/(double)CLOCKS_PER_SEC, 
				increased, NUM_THREADS * NUM_BLOCKS * CHECK_POINT,
				dart.getNextFront(), numSamples, dart.getNumFront());
#ifdef CUDA_MEASURE_TIME
			double trialsPerSec = (NUM_THREADS * NUM_BLOCKS * CHECK_POINT) / ((time - elapsed)/(double)CLOCKS_PER_SEC);
			printf("%f trials / sec\n", trialsPerSec);
			printf("Total iteration time : %f ms\n", (time - elapsed)/(double)CLOCKS_PER_SEC / CHECK_POINT * 1000);
			printf("Setup Time : %f ms\n", msSetup);
			printf("Update Interval Time : %f ms\n", msUpdateInterval);
			printf("Update Interval 2 Time : %f ms\n", msUpdateInterval2);
			printf("Pick Time : %f ms\n", msPickPoint);
			printf("Add Sample Time : %f ms\n", msAdd);
#endif

			if ((numSamples - oldSampleSize) < CHECK_POINT * 100 && !dart.getUseMultipleBlocks())
			{
				printf("start to use multiple blocks\n");
				dart.setUseMultipleBlocks(true);
				numBlocks = NUM_BLOCKS;
			}

			oldSampleSize = numSamples;
			elapsed = time;

			if((time - printTime)/(double)CLOCKS_PER_SEC > timeOut)
				break;
		}
		if (dart.terminationCheck())
			break;
	}
	totalTime = clock() - startTime;

	cudaEventDestroy(startEvent);
	cudaEventDestroy(stopEvent);

	dart.downloadSamples();
	printf("%d Iterations\n", iterations);
	printf("Total Time : %f\n", totalTime / (double)CLOCKS_PER_SEC);
	printf("Setup Time : %f\n", setupTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval Time : %f\n", updateIntervalTime / (double)CLOCKS_PER_SEC);
	printf("Update Interval2 Time : %f\n", updateInterval2Time / (double)CLOCKS_PER_SEC);
	printf("Pick Point Time : %f\n", pickPointTime / (double)CLOCKS_PER_SEC);
	printf("Add Sample Time : %f\n", addSampleTime / (double)CLOCKS_PER_SEC);
}